﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.ServiceModel;
using System.Threading;
using System.Windows.Forms;
using Bioinformatics.Types;
using Bioinformatics.UI.eUtils;
using Bioinformatics.UI.eUtilsFetchSequence;
using Bioinformatics.UI.Properties;
using eUtilsServiceSoapClient = Bioinformatics.UI.eUtilsFetchSequence.eUtilsServiceSoapClient;

namespace Bioinformatics.UI
{
    public partial class BrowserForm : Form
    {
        private readonly GeneticSequenceCollection _availableSequences;
        private readonly List<GeneticSequenceDownload> _currentSequences = new List<GeneticSequenceDownload>();
        private readonly BindingSource _bindingSource = new BindingSource();
        
        public BrowserForm()
        {
            //initialize user controls
            InitializeComponent();

            //import sequences
            _availableSequences =
                GeneticSequenceCollection.ImportCollectionFromFile(Environment.CurrentDirectory + "\\repository.xml");
           
            uxDataGrid.AutoGenerateColumns = false;
                        
            
            //pragmatically define gridview
            uxDataGrid.DataSource = _bindingSource;
            _bindingSource.DataSource = _currentSequences;
            
            uxDataGrid.AutoResizeColumns();
        }

        private GeneticSequenceDownload retreiveSequenceSummary(string sequenceId, string database, GeneticSequenceDownload speciesInfo)
        {
            var summaryServer = new eUtils.eUtilsServiceSoapClient();
            eSummaryResult summaryResult = summaryServer.run_eSummary(new eSummaryRequest {db = database, id = sequenceId, retmax = "100"});
            return new GeneticSequenceDownload
                       {
                           Name = speciesInfo.Name,
                           CommonName = speciesInfo.CommonName,
                           TaxonomyId = speciesInfo.TaxonomyId,
                           Caption = summaryResult.DocSum[0].Item[0].ItemContent,
                           Description = summaryResult.DocSum[0].Item[1].ItemContent,
                           Gi = summaryResult.DocSum[0].Item[3].ItemContent,
                           Length = long.Parse(summaryResult.DocSum[0].Item[8].ItemContent)
                       };
        }

        private GeneticSequenceDownload retreiveTaxonomySummary(string sequenceId)
        {
            var summaryServer = new eUtils.eUtilsServiceSoapClient();
            eSummaryResult summaryResult = summaryServer.run_eSummary(new eSummaryRequest {db = "taxonomy", id = sequenceId, retmax = "100"});
            return new GeneticSequenceDownload
                       {
                           Name = summaryResult.DocSum[0].Item[2].ItemContent,
                           CommonName = summaryResult.DocSum[0].Item[3].ItemContent,
                           TaxonomyId = summaryResult.DocSum[0].Item[4].ItemContent
                       };
        }

        private void searchCompleted(IAsyncResult result)
        {
            uxSearchTerms.Invoke(new MethodInvoker(() => { uxSearchTerms.Enabled = true; }));
            uxFetch.Invoke(new MethodInvoker(() => { uxFetch.Enabled = true; }));
            uxStatusLabel.Invoke(new MethodInvoker(() => { uxStatusLabel.Visible = false; }));
        }

        private void searchEntrez()
        {
            try
            {
                //ensure error notification is hidden
                showErrorNotification(false, string.Empty);
                //lookup taxonomy ids
                setStatusLabel("Retrieving taxonomy records...");
                string[] speciesIds = searchTaxonomy(uxSearchTerms.Text);

                if (speciesIds.Length == 0)
                    showErrorNotification(true, "Term not found");
                else
                    for (int s = 0; s < speciesIds.Length; s++)
                    {
                        GeneticSequenceDownload speciesInfo = retreiveTaxonomySummary(speciesIds[s]);

                        //find available sequences
                        if (Settings.Default.UseEst)
                        {
                            setStatusLabel("Querying Expressed Sequence Tag Database...");
                            string[] estIds = searchSequences(speciesIds[s], "nucest");
                            for (int seq = 0; seq < estIds.Length; seq++)
                                _currentSequences.Add(retreiveSequenceSummary(estIds[seq], "nucest", speciesInfo));
                        }
                        //find available sequences
                        if (Settings.Default.UsePopSet)
                        {
                            setStatusLabel("Querying PopSet Database...");
                            string[] popsetsIds = searchSequences(speciesIds[s], "popset");
                            for (int seq = 0; seq < popsetsIds.Length; seq++)
                                _currentSequences.Add(retreiveSequenceSummary(popsetsIds[seq], "popset", speciesInfo));
                        }
                        //find available sequences
                        if (Settings.Default.UseNucleotide)
                        {
                            setStatusLabel("Querying Nucleotide Database...");
                            string[] nucleotideIds = searchSequences(speciesIds[s], "nucleotide");
                            for (int seq = 0; seq < nucleotideIds.Length; seq++)
                                _currentSequences.Add(retreiveSequenceSummary(nucleotideIds[seq], "nucleotide", speciesInfo));
                        }
                        //find available sequences
                        if (Settings.Default.UseGss)
                        {
                            setStatusLabel("Querying Genome Sequence Survey Database...");
                            string[] gssIds = searchSequences(speciesIds[s], "nucgss");
                            for (int seq = 0; seq < gssIds.Length; seq++)
                                _currentSequences.Add(retreiveSequenceSummary(gssIds[seq], "nucgss", speciesInfo));
                        }
                        //find available sequences
                        if (Settings.Default.UseGenome)
                        {
                            setStatusLabel("Querying Genome Database...");
                            string[] genomeIds = searchSequences(speciesIds[s], "genome");
                            for (int seq = 0; seq < genomeIds.Length; seq++)
                                _currentSequences.Add(retreiveSequenceSummary(genomeIds[seq], "genome", speciesInfo));
                        }
                        //find available sequences
                        if (Settings.Default.UseProtein)
                        {
                            setStatusLabel("Querying Protein Database...");
                            string[] proteinIds = searchSequences(speciesIds[s], "protein");
                            for (int seq = 0; seq < proteinIds.Length; seq++)
                                _currentSequences.Add(retreiveSequenceSummary(proteinIds[seq], "protein", speciesInfo));
                        }
                        uxDataGrid.Invoke(new MethodInvoker(() =>
                                                                {
                                                                    uxDataGrid.DataSource = null;
                                                                    uxDataGrid.DataSource = _currentSequences;
                                                                }));
                    }
            }
            catch (EndpointNotFoundException ex)
            {
                showErrorNotification(true, "Connection Error");
            }
            catch (ProtocolException ex)
            {
                showErrorNotification(true, "Web Service Error");
            }
            catch (ServerTooBusyException ex)
            {
                showErrorNotification(true, "Web Server Busy, Please Try Later");
            }
        }

        private string[] searchSequences(string taxaId, string database)
        {
            var sequenceServer = new eUtils.eUtilsServiceSoapClient();
            eSearchResult sequenceResult = sequenceServer.run_eSearch(new eSearchRequest {db = database, term = "txid" + taxaId});
            return sequenceResult.IdList;
        }

        private string[] searchTaxonomy(string searchTerm)
        {
            var taxonomyServer = new eUtils.eUtilsServiceSoapClient();
            eSearchResult taxIdResults = taxonomyServer.run_eSearch(new eSearchRequest {db = "taxonomy", term = uxSearchTerms.Text, RetMax = "100"});
            return taxIdResults.IdList;
        }

        private void setStatusLabel(string text)
        {
            uxStatusLabel.Invoke(new MethodInvoker(() => { uxStatusLabel.Text = text; }));
        }

        private void showErrorNotification(bool visible, string label)
        {
            uxNotFoundLabel.Invoke(new MethodInvoker(() =>
                                                         {
                                                             uxNotFoundLabel.Text = label;
                                                             uxNotFoundLabel.Visible = visible;
                                                         }));
            uxNotFoundImage.Invoke(new MethodInvoker(() => uxNotFoundImage.Visible = visible));
        }

        private void BrowserForm_Resize(object sender, EventArgs e)
        {
            uxDataGrid.AutoResizeColumns();
        }


        private void uxFetch_Click(object sender, EventArgs e)
        {
            uxSearchTerms.Enabled = uxFetch.Enabled = false;
            uxStatusLabel.Visible = true;

            var methodInvoker = new MethodInvoker(searchEntrez);
            methodInvoker.BeginInvoke(searchCompleted, null);
        }

        private void uxSettingsButton_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            var settingsForm = new SettingsForm {MaximumSequencePacketLength = Settings.Default.MaximumPacketSequenceLength, Genome = Settings.Default.UseGenome, PopSet = Settings.Default.UsePopSet, Gss = Settings.Default.UseGss, Est = Settings.Default.UseEst, Protein = Settings.Default.UseProtein, Nucleotide = Settings.Default.UseNucleotide};

            if (settingsForm.ShowDialog() == DialogResult.OK)
            {
                Settings.Default.MaximumPacketSequenceLength = settingsForm.MaximumSequencePacketLength;
                Settings.Default.UseEst = settingsForm.Est;
                Settings.Default.UseGenome = settingsForm.Genome;
                Settings.Default.UseGss = settingsForm.Gss;
                Settings.Default.UseNucleotide = settingsForm.Nucleotide;
                Settings.Default.UsePopSet = settingsForm.PopSet;
                Settings.Default.UseProtein = settingsForm.Protein;
                Settings.Default.Save();
            }
        }

        private void uxSearchTerms_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Return || e.KeyCode == Keys.Enter)
                uxFetch_Click(null, null);
        }

        private void uxAddSequence_Click(object sender, EventArgs e)
        {
            var addSequenceForm = new AddSequenceForm();

            if (addSequenceForm.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
               _availableSequences.Add(addSequenceForm.ManualSequence);
                _availableSequences.ExportCollectionToFile(Environment.CurrentDirectory + "\\repository.xml");
            }
        }

        private void uxDataGrid_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            //checks if the cell clicked is the download column and then adds item to thread pool for download
            if(e.ColumnIndex == 7)
                ThreadPool.QueueUserWorkItem(downloadSequence, _currentSequences[e.RowIndex]);
        }

        private void downloadSequence(object sequenceDownloadObject)
        {
            var sequenceDownload = sequenceDownloadObject as GeneticSequenceDownload;
            {
                var requestServer = new eUtilsServiceSoapClient();
                var segmentRequest = new eFetchRequest();
                segmentRequest.db = "genome";
                segmentRequest.id = sequenceDownload.Gi;
                segmentRequest.seq_start = "0";
                segmentRequest.seq_stop = sequenceDownload.Length.ToString();
                eFetchResult segmentResult = requestServer.run_eFetch(segmentRequest);
                
                //sequenceDownload.Status = "Downloading...";
                
                //var sequence = new char[sequenceDownload.Length];
                //var segmentCount = sequenceDownload.Length / 4096;
                //var remainderLength = sequenceDownload.Length % 4096;
                //object locker = new object();
                //var completedSegments = 0;

                ////download entire segments
                //for (var i = 0; i <= segmentCount; i++)
                //{
                //    var success = false;
                //    while (!success)
                //    {
                //        try
                //        {
                //            var requestServer = new eUtilsServiceSoapClient();
                //            var segmentRequest = new eFetchRequest();
                //            segmentRequest.db = "genome";
                //            segmentRequest.id = sequenceDownload.Gi;
                //            segmentRequest.seq_start = (i * 4096).ToString();
                //            segmentRequest.seq_stop = ((i + 1) * 4096).ToString();
                //            eFetchResult segmentResult = requestServer.run_eFetch(segmentRequest);
                //            requestServer.Close();
                //            Array.Copy(segmentResult.GBSet.GBSeq[0].GBSeq_sequence.ToCharArray(), 0, sequence, i * 4096, 4096);
                //            lock (locker)
                //            {
                //                completedSegments++;
                //                sequenceDownload.Status = "Downloading (" + (((float)(completedSegments++) / segmentCount) * 100).ToString("N0") + "%)";
                //            }
                //            success = true;
                //        }
                //        catch (Exception)
                //        {
                //            success = false;
                //        }
                //    }
                //}
            }
        }
    }
}